/* Copyright (c) 2019 285424336@qq.com.
 * All rights reserved.
 * Author: 285424336
 * License: LGPLV3
 */
#include <glib.h>

#include <cstdlib>
#include <memory>

#ifdef G_OS_WIN32
#ifdef USE_VLD
#include <vld.h>
#endif
#endif

static std::shared_ptr<GMainLoop> main_loop;
static std::shared_ptr<GMainLoop> thread_main_loop;
static GThread* g_thread = nullptr;
int g_semaphore = 0;
GMutex g_mutex;
GCond g_cond;
GSource* g_main_source = nullptr;
GSource* g_thread_source = nullptr;

struct CustomSource {
    GSource source_;
    int message_id_;
};

static void create_thread();
static gboolean main_dispatch(GSource* source, GSourceFunc callback, gpointer user_data);
static gboolean thread_dispatch(GSource* source, GSourceFunc callback, gpointer user_data);

/**
 * @brief main 程序入口函数
 * @param argc 启动参数个数
 * @param argv 启动参数数组
 * @return 程序返回值
 */
int main(int argc, char** argv) {
    (void)argc;
    (void)argv;
    g_mutex_init(&g_mutex);
    g_cond_init(&g_cond);
    main_loop.reset(g_main_loop_new(nullptr, FALSE), [](GMainLoop* loop) {
        if (nullptr != loop) {
            g_main_loop_unref(loop);
        }
    });
    g_assert_nonnull(main_loop.get());
    GSourceFuncs source_funcs = {nullptr,
                                 nullptr,
                                 main_dispatch,
                                 nullptr,
                                 nullptr,
                                 nullptr};
    g_main_source = g_source_new(&source_funcs, sizeof(CustomSource));
    g_assert_nonnull(g_main_source);
    g_source_attach(g_main_source, g_main_context_default());
    g_source_unref(g_main_source);
    create_thread();
    g_main_loop_run(main_loop.get());
    g_thread_join(g_thread);
    g_thread_unref(g_thread);
    g_cond_clear(&g_cond);
    g_mutex_clear(&g_mutex);
    return EXIT_SUCCESS;
}

gpointer thread_func(gpointer data) {
    (void)data;
    g_log(__FUNCTION__, G_LOG_LEVEL_MESSAGE, "thread enter\n");
    g_usleep(1000);
    g_mutex_lock(&g_mutex);
    if (++g_semaphore <= 0) {
        g_cond_signal(&g_cond);
    }
    g_mutex_unlock(&g_mutex);
    std::shared_ptr<GMainContext> thread_main_context(g_main_context_new(), [](GMainContext* main_context) {
        if (nullptr != main_context) {
            g_main_context_unref(main_context);
        }
    });
    thread_main_loop.reset(g_main_loop_new(thread_main_context.get(), FALSE), [](GMainLoop* loop) {
        if (nullptr != loop) {
            g_main_loop_unref(loop);
        }
    });
    GSourceFuncs source_funcs = {nullptr,
                                 nullptr,
                                 thread_dispatch,
                                 nullptr,
                                 nullptr,
                                 nullptr};
    g_thread_source = g_source_new(&source_funcs, sizeof(CustomSource));
    g_assert_nonnull(g_thread_source);
    g_source_attach(g_thread_source, thread_main_context.get());
    g_source_unref(g_thread_source);
    reinterpret_cast<struct CustomSource*>(g_main_source)->message_id_ = 1;
    g_source_set_ready_time(g_main_source, 0);
    g_main_loop_run(thread_main_loop.get());
    return nullptr;
}

static void create_thread() {
    g_thread = g_thread_new("thread", thread_func, nullptr);
    g_assert_nonnull(g_thread);
    g_mutex_lock(&g_mutex);
    if (--g_semaphore < 0) {
        g_log(__FUNCTION__, G_LOG_LEVEL_MESSAGE, "wait thread enter\n");
        g_cond_wait(&g_cond, &g_mutex);
    }
    g_log(__FUNCTION__, G_LOG_LEVEL_MESSAGE, "thread already enter\n");
    g_mutex_unlock(&g_mutex);
}

static gboolean main_dispatch(GSource* source, GSourceFunc callback, gpointer user_data) {
    (void)callback;
    (void)user_data;
    g_assert_nonnull(source);
    g_log(__FUNCTION__, G_LOG_LEVEL_MESSAGE, "received thread message: %d\n", reinterpret_cast<struct CustomSource*>(source)->message_id_);
    reinterpret_cast<struct CustomSource*>(g_thread_source)->message_id_ = 2;
    g_source_set_ready_time(g_thread_source, 0);
    g_main_loop_quit(main_loop.get());
    return G_SOURCE_REMOVE;
}

static gboolean thread_dispatch(GSource* source, GSourceFunc callback, gpointer user_data) {
    (void)callback;
    (void)user_data;
    g_assert_nonnull(source);
    g_log(__FUNCTION__, G_LOG_LEVEL_MESSAGE, "received main message: %d\n", reinterpret_cast<struct CustomSource*>(source)->message_id_);
    g_main_loop_quit(thread_main_loop.get());
    g_usleep(1000);
    return G_SOURCE_REMOVE;
}
